// Fill out your copyright notice in the Description page of Project Settings.

#include "StSocketClient.h"


////#include <iostream>
//#include <windows.h>


#include "AllowWindowsPlatformTypes.h"
#include <WS2tcpip.h>
#include <winsock.h>
#include <mstcpip.h> 
#include "HideWindowsPlatformTypes.h"

#include "OutputDeviceRedirector.h"

#pragma comment(lib, "ws2_32.lib") 

//using std::cout;
//using std::endl;
//using std::cin;
using std::string;


StSocketClient::StSocketClient(int id):linkID(id)
{

	memset(srverIP, 0, 4);
	srverHot = 0;
	sock = 0;
	run = inThreadRun = Linking = false;
	//cout << "StSocketClient::StSocketClient()" << endl;

}

StSocketClient::~StSocketClient() 
{
	run = false;
}

void StSocketClient::CloseSocket()
{
	mlock.lock();
	sendMessageList.clear();
	recvMessageList.clear();
	mlock.unlock();

	run = false;

	if (sock > 0)
	{
		int maxTime = 100;
		while (inThreadRun)//等待线程退出
		{
			GLog->Log("CloseSocket...");
			maxTime--;
			if (maxTime <= 0)break;
			Sleep(100);
		}
	
	}

	return;
}



void StSocketClient::InitLink(int ipa, int ipb, int ipc, int ipd, int hot) 
{
	srverIP[0] = ipa;
	srverIP[1] = ipb;
	srverIP[2] = ipc;
	srverIP[3] = ipd;
	srverHot = hot;
	ipBuffer = stsocket::IntToString(ipa) + "." + stsocket::IntToString(ipb) + "." + stsocket::IntToString(ipc) + "." + stsocket::IntToString(ipd);
}


//std::thread
void StSocketClient::StartLink()
{
	CloseSocket();
	run = true;
	Linking = false;
	socketThread = std::thread(&StSocketClient::ThreadRun, std::ref(*this));
	socketThread.detach();
	//连接成功添加启动

}


void StSocketClient::ThreadRun()
{
	inThreadRun = true;

	Sleep(100);

	bool need = true;
	WSADATA wsaData;
	WORD sockVersion = MAKEWORD(2, 2);
	sockaddr_in sin;
	ULONG saddr;

	//windows网络编程库的版本号信息
	if (WSAStartup(sockVersion, &wsaData) != 0)
		//WSAStartup函数是在程序中初始化并加载Windows网络
	{
	
		need = false;
		//如果WSAStartup返回值为1的话就表示ws2_32.dll文件有问题，程序退出
	}
	else 
	{
		need = true;
		sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	}

	if (need)
	{
		//在本机上创建一个socket  
		if (sock == INVALID_SOCKET)  //判断socket是否创建成功
		{
			need = false;	 //如果创建失败就在程序中返回0结束程序
		}
		else
		{
			need = true;
			//创建一个socket编程类型的网络地址数据结构以便connect函数对本机创建的socket数据
			//结构进行初始化。
			sin.sin_family = AF_INET;   //设置网络协议族类型
			sin.sin_port = htons(srverHot);  //设置远程计算机的端口号
											 //sin.sin_addr.S_un.S_addr = inet_addr("192.168.0.178");//设置远程计算机的IP地址 

			inet_pton(AF_INET, ipBuffer.c_str(), &saddr);
			sin.sin_addr.S_un.S_addr = saddr;
		}
	}


	if (need)
	{
		if (connect(sock, (sockaddr*)&sin, sizeof(sockaddr)) == -1)
			//初始化socket并连接远程计算机
		{
			need = false;		
		}
		else
		{
			//heartTage.SetAddTime(1000);//一段时间后开始心跳
			int iMode = 1;
			int err = ioctlsocket(sock, FIONBIO, (u_long FAR*) &iMode);	//设置Socket为非阻塞模式
			if (SOCKET_ERROR == err) 
			{
				need = false;
			}
			//开启Keepalive 
			/***************************************开启keepalivetime 检查异常断开***************************************************/
			tcp_keepalive keepinfo_In = { 0 };
			tcp_keepalive keepinfo_Out = { 0 };
			keepinfo_In.onoff = 1;// // 开启keepalive属性
			keepinfo_In.keepalivetime = 16 * 1000;  // 开始首次KeepAlive探测前的TCP空闭时间 5秒
			keepinfo_In.keepaliveinterval = 4 * 1000;  // 两次KeepAlive探测间的时间间隔  8秒  
													   //win10一般情况下探测8次左右   预期16+4*8 = 48秒后检测到异常断开
			unsigned long ulBytesReturn = 0;

			WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepinfo_In, sizeof(keepinfo_In), &keepinfo_Out, sizeof(keepinfo_Out), &ulBytesReturn, NULL, NULL);

		}
	}


	bool sendOrRecv = false;
	StCommunicationTyp ctyp;
	if (need)
	{
		Linking = true;
		while (sock != 0&&run)
		{
			Sleep(100);
			sendOrRecv = !sendOrRecv;

			if (sendOrRecv) 
			{
				ctyp = SendOneMessage();
			}
			else 
			{
				ctyp = RecvOneMessage();
			}		

			if (StCommunicationTyp::CommunicationFail == ctyp) 
			{
				break;
			}

		}
	}

	closesocket(sock);  //关闭这个TCP网络通信连接
	WSACleanup();  //释放ws2_32.dll动态库

	Linking = false;
	inThreadRun = false;

}

int StSocketClient::AddOneMessage(FString mes)
{
	string buff = TCHAR_TO_UTF8(*mes);
	int size = 0;
	mlock.lock();
	sendMessageList.push_back(stsocket::FSocketMessge(linkID, buff));
	size =  sendMessageList.size();
	mlock.unlock();
	return size;

}

int StSocketClient::GetMessageNum()
{
	int size = 0;
	mlock.lock();
	size = sendMessageList.size();
	mlock.unlock();
	return size;
}


bool StSocketClient::GetOneMessage(FString& mes)
{
	
	bool b = false;
	mlock.lock();
	if (recvMessageList.size() >= 1) 
	{
		mes = FString(recvMessageList[0].message.c_str());
		recvMessageList.erase(recvMessageList.begin());
		b = true;
	}
	mlock.unlock();
	return b;
}


StCommunicationTyp StSocketClient::SendOneMessage()
{
	stsocket::FSocketMessge buf;

	if (sendMessageList.size() >= 1)
	{
		mlock.lock();
		buf = sendMessageList[0];
		sendMessageList.erase(sendMessageList.begin());
		mlock.unlock();

		if (buf.message.size() <= 0) //消息太短不再发送
		{
			return StCommunicationTyp::NoCommunication;
		}
		//else
		//{
		//	heartTage.SetAddTime(30000);//一秒后下次心跳；
		//}
	}
	else
	{
		//if (heartTage.needTrigger())
		//{	
		//	buf = stsocket::FSocketMessge(linkID, STHEART, 12);
		//}
		//else
		//{
			return StCommunicationTyp::NoCommunication;
		//}
	}

	int  nRecv = 0;

	nRecv = send(sock, buf.message.c_str(), buf.message.size(), 0);

	DWORD err = GetLastError();


	if (0<nRecv)
	{	
		return StCommunicationTyp::CommunicationOK;
		GLog->Log("send data:\r\n " + FString::FromInt(nRecv) + FString(buf.message.c_str()) + "\r\nlen="+FString::FromInt(buf.message.size()));
	}
	else
	{
		if (0 == nRecv && 0 == err)
		{
			return StCommunicationTyp::CommunicationFail;
		}

		if (WSAEWOULDBLOCK != err)
		{
			return StCommunicationTyp::CommunicationFail;
		}	
	}
	return StCommunicationTyp::NoCommunication;
}

StCommunicationTyp StSocketClient::RecvOneMessage()
{
	char buffer[32] = "\0";  //声明一个从网络接收数据的缓存区
	int  nRecv = 0;  //声明一个从网络接收到的数据字节长度   
	nRecv = recv(sock, buffer, 32, 0);
	DWORD err = GetLastError();
	//recv是一个接收网络的TCP数据包函数，nRecv是从网络接收到数
	//据的字节长度 
	if (nRecv > 0)
	{
		FString buf = FString(buffer);
		GLog->Log("recv buff:\r\n " + buf + "\r\n line = " + FString::FromInt(buf.Len()));
		//如果接收到网络数据包长度大于0的话就在接收到的数据包未尾添加一个字符串
		//结束符  
		recvbuff.Append(buf);
		FString l, r;
		if (recvbuff.Split(TEXT("\r\n\r\n"), &l, &r) && l.Len() >= 1)
		{
			GLog->Log("recv one Data:\r\n  >>>>>>>>>>>>>>>>>" + l+"<<<<<<<<<<<<<<<<<<<<<<");
			recvbuff = r;

			mlock.lock();
			recvMessageList.push_back(stsocket::FSocketMessge(linkID, TCHAR_TO_UTF8(*l)));
			mlock.unlock();
		}
		

		//	heartTage.SetAddTime(30000);//一秒后下次心跳；
		//}
	}
	else
	{
		if (0 == nRecv && 0 == err)
		{
			return StCommunicationTyp::CommunicationFail;
		}
		if (SOCKET_ERROR == nRecv && WSAEWOULDBLOCK != err)
		{
			return StCommunicationTyp::CommunicationFail;

		}
		return StCommunicationTyp::NoCommunication;
	}

	return StCommunicationTyp::CommunicationOK;
}

void  StSocketClient::WriteRecvMessage(const std::string& mes) 
{
	//按字符串格式输出接收到的数据 
	recvMessageList.push_back(stsocket::FSocketMessge(linkID, mes));

}

bool StSocketClient::GetIp(int index, unsigned long& lineIp, int& a, int& b, int& c, int& d, FString& pcName)
{
	string out;
	pcName.Empty();
	if (GetIp(index, lineIp, a, b, c, d, out)) 
	{
		pcName = FString(out.c_str());
		return true;
	}
	return false;
}

bool StSocketClient::GetIp(int index, unsigned long& lineIp, int& a, int& b, int& c, int& d, string& pcName)
{
	char hostname[MAX_PATH] = { 0 };
	gethostname(hostname, MAX_PATH);

	addrinfo hints;
	addrinfo* res;
	

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_INET; /* Allow IPv4 */
	hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
	hints.ai_protocol = 0; /* Any protocol */
	hints.ai_socktype = SOCK_STREAM;

	getaddrinfo(hostname, NULL, &hints, &res);
	pcName = string(hostname);

	GLog->Log("HoseIP==" + FString(pcName.c_str()));

	int num = 0;
	addrinfo* cur;
	for (cur = res; cur != NULL; cur = cur->ai_next)
	{
		sockaddr_in* addr = (sockaddr_in*)cur->ai_addr;

		char ipbuf[16];

		inet_ntop(AF_INET, &addr->sin_addr, ipbuf, 16);

		GLog->Log("HoseIP==" + FString(ipbuf));

		if (num == index)
		{
			lineIp = addr->sin_addr.S_un.S_addr;
			a = addr->sin_addr.S_un.S_un_b.s_b1;
			b = addr->sin_addr.S_un.S_un_b.s_b2;
			c = addr->sin_addr.S_un.S_un_b.s_b3;
			d = addr->sin_addr.S_un.S_un_b.s_b4;
			return true;
		}
		num++;
	}

	return false;

}


bool StSocketClient::GetIPFromDns(const FString& dns_, FString& outip)
{
	string out;
	string dns = TCHAR_TO_UTF8(*dns_);
	if (GetIPFromDns(dns, out))
	{
		outip = out.c_str();
		return true;
	}
	return false;
}



bool StSocketClient::GetIPFromDns(const std::string& dns, std::string& outip)
{
	outip.clear();

	addrinfo hint;
	addrinfo* answer;

	memset(&hint, sizeof(hint),0);
	hint.ai_family = AF_INET;
	hint.ai_socktype = SOCK_STREAM;
	int ret = ::getaddrinfo("ddns.jimujiaju.cn",NULL, NULL,&answer);
	if (ret == 0) 
	{
		addrinfo* curr;
		char ip[NI_MAXHOST] = { 0 };
		for (curr = answer; curr != NULL; curr = curr->ai_next) {
			inet_ntop(AF_INET,
				&(((sockaddr_in *)(curr->ai_addr))->sin_addr),
				ip, 16);
			outip = string(ip);
		}
	
	}
	else 
	{
		auto err = gai_strerror(ret);
		
	}

	freeaddrinfo(answer);

	return outip.size() >= 7;

}